//
// @Author: zhengqi Wu
// @Description:
// @Version: 1.0.0
// @Date: 2021/3/3 18:07
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

//
// 表示一个通用的MIN推式包
//
// @Description:
//	1. 包格式定义如下：
//		CPacket = 5 TLV-LENGTH
//             { CommonIdentifier }          => 标识区
//             { Signature }                 => 签名区
//             {                             => 只读区
//                 <Payload>
//             }
//             {                             => 可变区
//                 {                         => 受保护区
//                     "Empty"
//                 }
//                 {                         => 非受保护区
//                     [TTL]
//                 }
//             }
//

const MINPacket = require('./MINPacket')
const {TTL} = require('../component/TTL')
const {Payload} = require('../component/Payload')
const encoding = require('../encoding')
const {Identifier} = require('../component/Identifier')
const {createIdentifierWrapperByTypeAndIdentifier} = require('../component/IdentifierWrapper')


class CPacket extends encoding.IEncodingAble{
    constructor() {
        super();
        this.MINPacket = new MINPacket()
        this.SelfEncodingBase = new encoding.SelfEncodingBase()
        this.TTL = new TTL()
        this.Payload = new Payload()
        this.srcIdentifier = new Identifier()
        this.dstIdentifier = new Identifier()
    }

    getSrcIdentifier(){
        return this.srcIdentifier
    }

    setSrcIdentifier(srcIdentifier){
        this.srcIdentifier = srcIdentifier
    }

    getDstIdentifier(){
        return this.dstIdentifier
    }

    setDstIdentifier(dstIdentifier){
        this.dstIdentifier = dstIdentifier
    }

    extraDataFromFields() {
        this.doExtraDataFromFields(this.MINPacket)
    }

    /**
     * 从 MINPacket 的分区当中提取出 CPacket 的各项属性
     * DONE 整合四个Field与TTL Payload
     */
    doExtraDataFromFields(MINPacket) {
       
        try {
            // 提取 Payload
            let payloadBlock = MINPacket.ReadOnlyField.getBlockByType(encoding.tlv.ComponentType.TlvPayload)
            // DONE 抛出异常
            if (payloadBlock === undefined) {
                throw new CPacketError()
            }
            this.Payload.wireDecode(payloadBlock)

            // 提取 TTL
            let ttlBlock = MINPacket.MutableField.mutableDangerousField.getBlockByType(encoding.tlv.ComponentType.TlvTTL)
            // DONE 抛出异常
            if (ttlBlock === undefined) {
                throw new CPacketError()
            }
            this.TTL.wireDecode(ttlBlock)

            // 提取标识
            let commonIdentifier = MINPacket.IdentifierField.getIdentifierByType(encoding.tlv.ComponentType.TlvIdentifierCommon)
            // DONE 抛出异常
            if (commonIdentifier === undefined) {
                throw new CPacketError()
            }
            // 提取标识
            let srcIdentifier = MINPacket.IdentifierField.getIdentifier(0) // wrapper
            if (srcIdentifier === undefined) {
                throw new CPacketError()
            }
            let dstIdentifier = MINPacket.IdentifierField.getIdentifier(1) // wrapper
            if (dstIdentifier === undefined) {
                throw new CPacketError()
            }
            this.dstIdentifier = dstIdentifier.getIdentifier()
            this.srcIdentifier = srcIdentifier.getIdentifier()
        } catch (e) {
            throw e
        }
        
    }

    fillDataToFields() {
        this.doFillDataToFields(this.MINPacket)
    }
    /**
     * 将 CPacket 的各项属性填充到 MINPacket 中定义的对应分区当中
     * @Description:
     * 1. 主要填充标识区、只读区和可变区
     */
    doFillDataToFields(MINPacket) {
        // 可变区 => 受保护区（无）

        // 填充可变区 => 非受保护区
        // 首先清除所有的Block
        try {
            MINPacket.MutableField.mutableDangerousField.clearBlocks()
            let block = this.TTL.selfWireEncode(this.TTL)
            MINPacket.MutableField.mutableDangerousField.addBlock(block)

            // 填充只读区
            // 首先清除所有的Block
            MINPacket.ReadOnlyField.clearBlocks()
            block = this.Payload.selfWireEncode(this.Payload)
            MINPacket.ReadOnlyField.addBlock(block)

            // 填充标识区
            // 首先清除所有的标识
            MINPacket.IdentifierField.clearIdentifiers()
            // DONE 待整合identifier模块
            let srcIdentifierWrapper = createIdentifierWrapperByTypeAndIdentifier(encoding.tlv.ComponentType.TlvIdentifierCommon, this.srcIdentifier)
            MINPacket.IdentifierField.addIdentifier(srcIdentifierWrapper)
            let dstIdentifierWrapper = createIdentifierWrapperByTypeAndIdentifier(encoding.tlv.ComponentType.TlvIdentifierCommon, this.dstIdentifier)
            MINPacket.IdentifierField.addIdentifier(dstIdentifierWrapper)
        } catch (e) {
            throw e
        }
        
    }

    /**
     * 将 CPacket 编码成 TLV
     * @param enc {encoding.Encoder}
     * @returns 编码长度{number}
     */
    wireEncode(enc) {
        try {
            // 首先将属性填充到分区当中
            this.fillDataToFields()
            // 指定包类型
            this.MINPacket.packetType = encoding.tlv.PacketType.TlvPacketMINCommon
            return this.MINPacket.wireEncode(enc)
        } catch (e) {
            throw e
        }
        
    }

    /**
     * 从 TLV Block 中解码出一个 CPacket
     * @param block {encoding.block.Block}
     */
    wireDecode(block) {
        try {
            this.MINPacket.wireDecode(block)
            this.extraDataFromFields()
        } catch (e) {
            throw e
        }
        
    }

    /**
     *
     * @returns {string}
     */
    toUri() {
        return this.flatIdentifier
    }

    /**
     * 设置推式包的平面标识
     * @param identifier
     */
    setIdentifier(identifier) {
        this.flatIdentifier = identifier
    }

    /**
     * 获取推式包的平面标识
     * @returns {string}
     */
    getIdentifier() {
        return this.flatIdentifier
    }

    /**
     * 得到 TTL
     * @returns {*}
     */
    getTTL(){
        return this.TTL.ttl
    }

    /**
     * 设置 TTL
     */
    setTTL(ttl){
        this.TTL.setTTL(ttl)
    }
}

/**
 * CreateCPacketByMINPacket 根据一个 MINPacket 创建一个 CPacket
 * @param minPacket
 * @return {CPacket}
 */
function createCPacketByMINPacket(minPacket) {
    let cPacket = new CPacket()
    try {
        cPacket.doExtraDataFromFields(minPacket)
        return cPacket
    } catch (e) {
        throw e
    }
}

class CPacketError extends Error{
    constructor(m) {
        super(m)
        this.name = 'CPacketError'
    }

    error() {
        console.log(`${this.name}: ${this.message}`)
    }
}

module.exports = {
    CPacket: CPacket,
    createCPacketByMINPacket: createCPacketByMINPacket
}